/* Copyright (C) 2011 The University of Michigan This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Please send inquiries to powertutor@umich.edu */ package vn.cybersoft.obs.andriod.batterystats2.util; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Iterator; import java.util.Vector; import vn.cybersoft.obs.andriod.batterystats2.PowerNotifications; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.util.Log; @SuppressWarnings("unchecked") public class NotificationService { private static final String TAG = "NotificationService"; /* We haven't tried to install the hook yet. */ private static final int STATE_INIT = 0; /* The hook was installed successfully and we should be receiving power * related notifications from the battery service. */ private static final int STATE_HOOK_INSTALLED = 1; /* The hook failed to install. This should be the case for most phones as a * hack is required to get this to work. */ private static final int STATE_HOOK_FAILED = 2; private static int hookState = STATE_INIT; private static Binder notifier = new NotificationForwarder(); private static Vector<PowerNotifications> hooks = new Vector<PowerNotifications>(); private static Method methodGetService; static { try { Class classServiceManager = Class.forName("android.os.ServiceManager"); methodGetService = classServiceManager.getMethod("getService", String.class); } catch(NoSuchMethodException e) { Log.w(TAG, "Could not find method gerService"); } catch(ClassNotFoundException e) { Log.w(TAG, "Could not find class android.os.ServiceManager"); } } private static IBinder getBatteryService() { if(methodGetService == null) return null; try { return (IBinder)methodGetService.invoke(null, "batteryhook"); } catch(InvocationTargetException e) { Log.w(TAG, "Call to get service failed"); } catch(IllegalAccessException e) { Log.w(TAG, "Call to get service failed"); } return null; } public static boolean available() { synchronized(hooks) { if(hookState == STATE_INIT) { return getBatteryService() != null; } return hookState == STATE_HOOK_INSTALLED; } } public static void addHook(PowerNotifications notif) { synchronized(hooks) { if(hookState == STATE_INIT) { installHook(); } if(hookState != STATE_HOOK_INSTALLED) { Log.w(TAG, "Attempted to add hook though no " + "notification service available"); } else { hooks.add(notif); } } } public static void removeHook(PowerNotifications notif) { synchronized(hooks) { hooks.remove(notif); } } private static void installHook() { Parcel outBinder = Parcel.obtain(); outBinder.writeStrongBinder(notifier); hookState = STATE_HOOK_FAILED; try { IBinder batteryHook = getBatteryService(); if(batteryHook == null) { /* This should be the case on un-hacked phone. Maybe one day * phones will support this service or similar by default. */ Log.i(TAG, "No power notification hook service installed"); } else if(!batteryHook.transact(0, outBinder, null, 0)) { Log.w(TAG, "Failed to register forwarder"); } else { hookState = STATE_HOOK_INSTALLED; } } catch(RemoteException e) { Log.w(TAG, "Failed to register forwarder"); } outBinder.recycle(); } /* Class responsible for forwarding power notifications to registered * hooks. */ private static class NotificationForwarder extends DefaultReceiver { public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { synchronized(hooks) { for(Iterator<PowerNotifications> iter = hooks.iterator(); iter.hasNext(); ) { Parcel junk = Parcel.obtain(); try { iter.next().asBinder().transact(code, data, junk, flags); } catch(RemoteException e) { iter.remove(); } data.setDataPosition(0); junk.recycle(); } } return super.onTransact(code, data, reply, flags); } } /* If you only want to receive a subset of the notifications just extend this * class and override the methods you care about. */ public static class DefaultReceiver extends PowerNotifications.Stub { public void noteSystemMediaCall(int uid) {} public void noteStartMedia(int uid, int id) {} public void noteStopMedia(int uid, int id) {} public void noteVideoSize(int uid, int id, int width, int height) {} public void noteStartWakelock(int uid, String name, int type) {} public void noteStopWakelock(int uid, String name, int type) {} public void noteStartSensor(int uid, int sensor) {} public void noteStopSensor(int uid, int sensor) {} public void noteStartGps(int uid) {} public void noteStopGps(int uid) {} public void noteScreenOn() {} public void noteScreenBrightness(int brightness) {} public void noteScreenOff() {} public void noteInputEvent() {} public void noteUserActivity(int uid, int event) {} public void notePhoneOn() {} public void notePhoneOff() {} public void notePhoneDataConnectionState(int dataType, boolean hasData) {} public void noteWifiOn(int uid) {} public void noteWifiOff(int uid) {} public void noteWifiRunning() {} public void noteWifiStopped() {} public void noteBluetoothOn() {} public void noteBluetoothOff() {} public void noteFullWifiLockAcquired(int uid) {} public void noteFullWifiLockReleased(int uid) {} public void noteScanWifiLockAcquired(int uid) {} public void noteScanWifiLockReleased(int uid) {} public void noteWifiMulticastEnabled(int uid) {} public void noteWifiMulticastDisabled(int uid) {} public void setOnBattery(boolean onBattery, int level) {} public void recordCurrentLevel(int level) {} public void noteVideoOn(int uid) {} public void noteVideoOff(int uid) {} public void noteAudioOn(int uid) {} public void noteAudioOff(int uid) {} } /* Useful for debugging purposes. */ public static class PrintNotifications extends PowerNotifications.Stub { public void noteSystemMediaCall(int uid) { System.out.println("System media call[uid=" + uid + "]"); } public void noteStartMedia(int uid, int id) { System.out.println("Start media[uid=" + uid + ", id=" + id + "]"); } public void noteStopMedia(int uid, int id) { System.out.println("Stop media[uid=" + uid + ", id=" + id + "]"); } public void noteVideoSize(int uid, int id, int width, int height) { System.out.println("Video size[uid=" + uid + ", id=" + id + ", width=" + width + ", height=" + height + "]"); } public void noteStartWakelock(int uid, String name, int type) { System.out.println("Start wakelock[uid=" + uid + ", name=" + name + ", type=" + type + "]"); } public void noteStopWakelock(int uid, String name, int type) { System.out.println("Stop wakelock[uid=" + uid + ", name=" + name + ", type=" + type + "]"); } public void noteStartSensor(int uid, int sensor) { System.out.println("noteStartSensor[uid=" + uid + ", sensor=" + sensor + "]"); } public void noteStopSensor(int uid, int sensor) { System.out.println("noteStopSensor[uid=" + uid + ", sensor=" + sensor + "]"); } public void noteStartGps(int uid) { System.out.println("noteStartGps[uid=" + uid + "]"); } public void noteStopGps(int uid) { System.out.println("noteStopGps[uid=" + uid + "]"); } public void noteScreenOn() { System.out.println("noteScreenOn"); } public void noteScreenBrightness(int brightness) { System.out.println("noteScreenBrightness[brightness=" + brightness + "]"); } public void noteScreenOff() { System.out.println("noteScreenOff"); } public void noteInputEvent() { System.out.println("noteInputEvent"); } public void noteUserActivity(int uid, int event) { System.out.println("noteUserActivity[uid=" + uid + ", event=" + event + "]"); } public void notePhoneOn() { System.out.println("notePhoneOn"); } public void notePhoneOff() { System.out.println("notePhoneOff"); } public void notePhoneDataConnectionState(int dataType, boolean hasData) { System.out.println("notePhoneDataConnectionState[dataType=" + dataType + ", hasData=" + hasData + "]"); } public void notePhoneState(int phoneState) { System.out.println("notePhoneState[phoneState=" + phoneState + "]"); } public void noteWifiOn(int uid) { System.out.println("noteWifiOn[uid=" + uid + "]"); } public void noteWifiOff(int uid) { System.out.println("noteWifiOff[uid=" + uid + "]"); } public void noteWifiRunning() { System.out.println("noteWifiRunning"); } public void noteWifiStopped() { System.out.println("noteWifiStopped"); } public void noteBluetoothOn() { System.out.println("noteBluetoothOn"); } public void noteBluetoothOff() { System.out.println("noteBluetoothOff"); } public void noteFullWifiLockAcquired(int uid) { System.out.println("noteFullWifiLockAcquired[uid=" + uid + "]"); } public void noteFullWifiLockReleased(int uid) { System.out.println("noteFullWifiLockReleased[uid=" + uid + "]"); } public void noteScanWifiLockAcquired(int uid) { System.out.println("noteScanWifiLockAcquired[uid=" + uid + "]"); } public void noteScanWifiLockReleased(int uid) { System.out.println("noteScanWifiLockReleased[uid=" + uid + "]"); } public void noteWifiMulticastEnabled(int uid) { System.out.println("noteWifiMulticastEnabled[uid=" + uid + "]"); } public void noteWifiMulticastDisabled(int uid) { System.out.println("noteWifiMulticastDisabled[uid=" + uid + "]"); } public void setOnBattery(boolean onBattery, int level) { System.out.println("setOnBattery[onBattery=" + onBattery + ", level=" + level + "]"); } public void recordCurrentLevel(int level) { System.out.println("recordCurrentLevel[level=" + level + "]"); } public void noteVideoOn(int uid) { System.out.println("noteVideoOn[uid=" + uid + "]"); } public void noteVideoOff(int uid) { System.out.println("noteVideoOff[uid=" + uid + "]"); } public void noteAudioOn(int uid) { System.out.println("noteAudioOn[uid=" + uid + "]"); } public void noteAudioOff(int uid) { System.out.println("noteAudioOff[uid=" + uid + "]"); } } }